package org.sabayframework.mem.impl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.sabayframework.mem.BigBuffer;

/**
 * BigBuffer backed by a {@link java.nio.ByteBuffer}.
 * */
public final class BigByteBuffer implements BigBuffer {

	private final ByteBuffer buffer;

	public BigByteBuffer(ByteBuffer buffer) {
		super();
		this.buffer = buffer;
	}

	public long capacity() {
		return buffer.capacity();
	}

	public long position() {
		return buffer.position();
	}

	public BigBuffer clear() {
		buffer.clear();
		return this;
	}

	public BigBuffer flip() {
		buffer.flip();
		return this;
	}

	public boolean hasRemaining() {
		return buffer.hasRemaining();
	}

	public boolean hasArray() {
		return buffer.hasArray();
	}

	public byte[] array() {
		return buffer.array();
	}

	public int arrayOffset() {
		return buffer.arrayOffset();
	}

	public boolean isDirect() {
		return buffer.isDirect();
	}

	public BigBuffer duplicate() {
		return new BigByteBuffer(buffer.duplicate());
	}

	public BigBuffer asReadOnlyBuffer() {
		return isReadOnly() ? this : new BigByteBuffer(buffer.asReadOnlyBuffer());
	}

	public BigBuffer compact() {
		buffer.compact();
		return this;
	}

	public int compareTo(BigBuffer that) {
		return AbstractBigBuffer.compare(this, that);
	}

	public BigBuffer position(long newPosition) {
		buffer.position((int)newPosition);
		return this;
	}

	public long limit() {
		return buffer.limit();
	}

	public BigBuffer limit(long newLimit) {
		buffer.limit((int)newLimit);
		return this;
	}

	public BigBuffer rewind() {
		buffer.rewind();
		return this;
	}

	public long remaining() {
		return buffer.remaining();
	}

	public boolean isReadOnly() {
		return buffer.isReadOnly();
	}

	public ByteOrder order() {
		return buffer.order();
	}

	public BigBuffer slice() {
		return new BigByteBuffer(buffer.slice());
	}

	public byte get(final long index) {
		return buffer.get((int)index);
	}

	public void put(final long index,final byte value) {
		buffer.put((int)index, value);
	}

	public char getChar(final long index) {
		return buffer.getChar((int)index);
	}

	public void putChar(final long index,final char value) {
		buffer.putChar((int)index, value);
	}

	public short getShort(final long index) {
		return buffer.getShort((int)index);
	}

	public void putShort(final long index,final short value) {
		buffer.putShort((int)index, value);
	}

	public int getInt(final long index) {
		return buffer.getInt((int)index);
	}

	public void putInt(final long index,final int value) {
		buffer.putInt((int)index, value);
	}

	public float getFloat(final long index) {
		return buffer.getFloat((int)index);
	}

	public void putFloat(final long index,final float value) {
		buffer.putFloat((int)index, value);
	}

	public long getLong(final long index) {
		return buffer.getLong((int)index);
	}

	public void putLong(final long index,final long value) {
		buffer.putLong((int)index, value);
	}

	public double getDouble(final long index) {
		return buffer.getDouble((int)index);
	}

	public void putDouble(final long index,final double value) {
		buffer.putDouble((int)index, value);
	}

	public byte get() {
		return buffer.get();
	}

	public void put(byte b) {
		buffer.put(b);
	}

	public short getShort() {
		return buffer.getShort();
	}

	public void putShort(short value) {
		buffer.putShort(value);
	}

	public int getInt() {
		return buffer.getInt();
	}

	public void putInt(int value) {
		buffer.putInt(value);
	}

	public long getLong() {
		return buffer.getLong();
	}

	public void putLong(long value) {
		buffer.putLong(value);
	}

	public float getFloat() {
		return buffer.getFloat();
	}

	public void putFloat(float value) {
		buffer.putFloat(value);
	}

	public double getDouble() {
		return buffer.getDouble();
	}

	public void putDouble(double value) {
		buffer.putDouble(value);
	}

	public char getChar() {
		return buffer.getChar();
	}

	public void putChar(char value) {
		buffer.putChar(value);
	}

	public void get(byte[] dst) {
		buffer.get(dst);
	}

	public void get(byte[] dst,final int offset,final int length) {
		buffer.get(dst, offset, length);
	}

	public void put(byte[] src) {
		buffer.put(src);
	}

	public void put(byte[] src,final int offset,final int length) {
		buffer.put(src, offset, length);
	}

	public void get(final short[] dst) {
		for(int i=0;i<dst.length;++i) dst[i] = getShort();
	}

	public void get(final short[] dst,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) dst[i] = getShort();
	}

	public void put(final short[] src) {
		for(int i=0;i<src.length;++i) putShort(src[i]);
	}

	public void put(short[] src,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) putShort(src[i]);
	}

	public void get(char[] dst) {
		for(int i=0;i<dst.length;++i) dst[i] = getChar();
	}

	public void get(char[] dst,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) dst[i] = getChar();
	}

	public void put(final char[] src) {
		for(int i=0;i<src.length;++i) putChar(src[i]);
	}

	public void put(char[] src,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) putChar(src[i]);
	}

	public void get(int[] dst) {
		for(int i=0;i<dst.length;++i) dst[i] = getInt();
	}

	public void get(int[] dst,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) dst[i] = getInt();
	}

	public void put(final int[] src) {
		for(int i=0;i<src.length;++i) putInt(src[i]);
	}

	public void put(int[] src,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) putInt(src[i]);
	}

	public void get(float[] dst) {
		for(int i=0;i<dst.length;++i) dst[i] = getFloat();
	}

	public void get(float[] dst,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) dst[i] = getFloat();
	}

	public void put(final float[] src) {
		for(int i=0;i<src.length;++i) putFloat(src[i]);
	}

	public void put(float[] src,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) putFloat(src[i]);
	}

	public void get(long[] dst) {
		for(int i=0;i<dst.length;++i) dst[i] = getLong();
	}

	public void get(long[] dst,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) dst[i] = getLong();
	}

	public void put(final long[] src) {
		for(int i=0;i<src.length;++i) putLong(src[i]);
	}

	public void put(long[] src,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) putLong(src[i]);
	}

	public void get(double[] dst) {
		for(int i=0;i<dst.length;++i) dst[i] = getDouble();
	}

	public void get(double[] dst,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) dst[i] = getDouble();
	}

	public void put(final double[] src) {
		for(int i=0;i<src.length;++i) putDouble(src[i]);
	}

	public void put(double[] src,final int offset,final int length) {
		for(int i=offset;i<length + offset;++i) putDouble(src[i]);
	}

	@Override
	public void put(BigBuffer src) {
		if(src instanceof ReadOnlyBigBuffer){
			//get inner buffer
			src = ((ReadOnlyBigBuffer)src).getBuffer();
		}
		if(src instanceof BigByteBuffer){
			buffer.put(((BigByteBuffer)src).buffer);
		} else if(src.hasArray()) {
			byte[] array = src.array();
			final int bytesToCopy = (int)src.remaining();
			buffer.put(array, src.arrayOffset() + (int)src.position(), bytesToCopy);
	        this.position(bytesToCopy+this.position());
		} else {
			int remaining = (int)src.remaining();
			int bufSize = Math.min(remaining, 4096 * 8);
			byte[] buffer = new byte[bufSize];
			while(remaining > 0){
				final int bytesToCopy = Math.min(remaining, buffer.length);
				src.get(buffer, 0, bytesToCopy);
				this.put(buffer, 0, bytesToCopy);
				remaining -= bytesToCopy;
			}
		}
	}

	@Override
	public void put(ByteBuffer src) {
		buffer.put(src);
	}

	@Override
	public String toString() {
		return "BigByteBuffer [buffer=" + buffer + "]";
	}

	@Override
	public int hashCode() {
		return AbstractBigBuffer.hashCode(this);
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof BigBuffer)) return false;
		BigBuffer that = (BigBuffer)obj;
		return AbstractBigBuffer.compare(this, that)==0;
	}

	@Override
	public void destroy() {}
	
}
